<!-- author: xuett -->
<!-- date: 2024-03-21 14:52:00 -->
<!-- description:  -->
<template>
  <div ref="sceneRef" class="scene"></div>
</template>

<script setup>
import { ref, onMounted } from "vue";
import * as d3 from "d3";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import shanxi from "@/assets/map/shanxi.json";
import shanxiAll from "@/assets/map/shanxi-all.json";
import { MeshLineGeometry, MeshLineMaterial } from "meshline";

const sceneRef = ref(null);

// 以经纬度116，39为中心，进行投影的函数转换函数
const projection = d3.geoMercator().center([116, 39]).translate([0, 0, 0]);
let scene, camera, renderer, controls;

onMounted(() => {
  init();
});

// 初始化
const init = () => {
  createScene(); // 创建场景
  createLight(); // 创建光源
  createCamera(); // 创建相机
  createRender(); // 创建渲染器
  createControls(); // 创建控件对象
  render(); // 渲染

  //渲染模型
  operationData(shanxi);
};

// 创建场景
const createScene = () => {
  scene = new THREE.Scene();
  scene.add(new THREE.AxesHelper(20));
  // scene.add(new THREE.GridHelper(10, 10));
};
// 创建光源
const createLight = () => {
  //平行光
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
  directionalLight.position.set(-4, 8, 4);
  directionalLight.castShadow = true;
  //半球光,光源直接放置于场景之上，光照颜色从天空光线颜色颜色渐变到地面光线颜色。
  const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x080820, 0.6);
  hemisphereLight.position.set(0, 8, 0);
  scene.add(directionalLight);
  scene.add(hemisphereLight);
};
// 创建相机
const createCamera = () => {
  const element = sceneRef.value;
  //透视相机
  camera = new THREE.PerspectiveCamera(45, element.clientWidth / element.clientHeight, 0.1, 1000);
  camera.position.set(0, 0, 80); // 设置相机位置
  camera.lookAt(scene.position);
};

// 创建渲染器
const createRender = () => {
  const element = sceneRef.value;
  //antialias - 是否执行抗锯齿。默认为false.  alpha - canvas是否包含alpha (透明度)。默认为 false
  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  renderer.setSize(element.clientWidth, element.clientHeight); // 设置渲染区域尺寸
  renderer.shadowMap.enabled = true; // 显示阴影
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  renderer.setClearColor(0x3f3f3f, 1); // 设置背景颜色
  element.appendChild(renderer.domElement);
};
// 创建控件对象
const createControls = () => {
  controls = new OrbitControls(camera, renderer.domElement);
  //启用或禁用相机的缩放
  // controls.enableZoom = false;
  //启用或禁用相机平移
  // controls.enablePan = false;
  //垂直轨道旋转角度
  // controls.maxPolarAngle = Math.PI / 2.3;
};
const render = () => {
  renderer.render(scene, camera);
  requestAnimationFrame(render);
  controls.update();
};

function operationData(jsondata) {
  // 全国信息
  const map = new THREE.Object3D();
  const features = jsondata.features;

  features.forEach((feature) => {
    // 单个省份 对象
    const province = new THREE.Object3D();
    // 地址
    province.properties = feature.properties.name;
    const coordinates = feature.geometry.coordinates;
    const color = "#99ff99";

    if (feature.geometry.type === "MultiPolygon") {
      // 多个，多边形
      coordinates.forEach((coordinate) => {
        // console.log(coordinate);
        // coordinate 多边形数据
        coordinate.forEach((rows) => {
          const mesh = drawExtrudeMesh(rows, color);
          const line = lineDraw(rows, color);
          // 唯一标识
          mesh.properties = feature.properties.name;

          province.add(line);
          province.add(mesh);
        });
      });
    }

    if (feature.geometry.type === "Polygon") {
      // 多边形
      coordinates.forEach((coordinate) => {
        const mesh = drawExtrudeMesh(coordinate, color);
        const line = lineDraw(coordinate, color);
        // 唯一标识
        mesh.properties = feature.properties.name;

        province.add(line);
        province.add(mesh);
      });
    }
    map.add(province);
  });
  map.scale.set(2, 2, 1);
  scene.add(map);
}

function lineDraw(polygon) {
  const lineGeometry = new THREE.BufferGeometry();
  const pointsArray = new Array();
  polygon.forEach((row) => {
    const [x, y] = projection(row);
    // 创建三维点
    pointsArray.push(new THREE.Vector3(x, -y, 5));
  });
  // 放入多个点
  lineGeometry.setFromPoints(pointsArray);
  // 生成随机颜色
  const lineColor = new THREE.Color(Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5);

  const lineMaterial = new THREE.LineBasicMaterial({
    color: lineColor
  });
  return new THREE.Line(lineGeometry, lineMaterial);
}

// 根据经纬度坐标生成物体
function drawExtrudeMesh(polygon) {
  const shape = new THREE.Shape();
  polygon.forEach((row, i) => {
    const [x, y] = projection(row);
    // console.log(row, [x, y]);
    if (i === 0) {
      // 创建起点,使用moveTo方法
      // 因为计算出来的y是反过来，所以要进行颠倒
      shape.moveTo(x, -y);
    }
    shape.lineTo(x, -y);
  });

  // 拉伸
  const geometry = new THREE.ExtrudeGeometry(shape, {
    depth: 5,
    bevelEnabled: true
  });

  // 随机颜色
  const randomColor = (0.5 + Math.random() * 0.5) * 0xffffff;
  const material = new THREE.MeshBasicMaterial({
    color: randomColor,
    transparent: true,
    opacity: 0.5
  });
  return new THREE.Mesh(geometry, material);
}
</script>

<style scoped lang="scss">
.scene {
  width: 100%;
  height: 100%;
}
</style>
